package elitech.vietnam.copyweatherfashion.widget;

import java.util.ArrayList;
import java.util.WeakHashMap;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Pair;
import android.widget.TextView;
import elitech.vietnam.copyweatherfashion.R;

public class MagicTextView extends TextView {
        private ArrayList<Shadow> outerShadows;
        private ArrayList<Shadow> innerShadows;
        
        private WeakHashMap<String, Pair<Canvas, Bitmap>> canvasStore;
        
        private Canvas tempCanvas;
        private Bitmap tempBitmap;
        
        private Drawable foregroundDrawable;
        
        private float strokeWidth;
        private Integer strokeColor;
        private Join strokeJoin;
        private float strokeMiter;
        
        private int[] lockedCompoundPadding;
        private boolean frozen = false;

        public MagicTextView(Context context) {
                super(context);
                init(null);
        }
        public MagicTextView(Context context, AttributeSet attrs) {
                super(context, attrs);
                init(attrs);
        }
        public MagicTextView(Context context, AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
                init(attrs);
        }
        
        public void init(AttributeSet attrs){
                outerShadows = new ArrayList<Shadow>();
                innerShadows = new ArrayList<Shadow>();
                if(canvasStore == null){
                    canvasStore = new WeakHashMap<String, Pair<Canvas, Bitmap>>();
                }
        
                if(attrs != null){
                        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MagicTextView);
                        
            String typefaceName = a.getString( R.styleable.MagicTextView_typeface);
            if(typefaceName != null) {
                Typeface tf = Typeface.createFromAsset(getContext().getAssets(), String.format("fonts/%s.ttf", typefaceName));
                setTypeface(tf);
            }
            
                        if(a.hasValue(R.styleable.MagicTextView_foreground)){
                                Drawable foreground = a.getDrawable(R.styleable.MagicTextView_foreground);        
                                if(foreground != null){
                                        this.setForegroundDrawable(foreground);
                                }else{
                                        this.setTextColor(a.getColor(R.styleable.MagicTextView_foreground, 0xff000000));
                                }
                        }
                
                        if(a.hasValue(R.styleable.MagicTextView_background)){
                                Drawable background = a.getDrawable(R.styleable.MagicTextView_background);
                                if(background != null){
                                        this.setBackgroundDrawable(background);
                                }else{
                                        this.setBackgroundColor(a.getColor(R.styleable.MagicTextView_background, 0xff000000));
                                }
                        }
                        
                        if(a.hasValue(R.styleable.MagicTextView_innerShadowColor)){
                                this.addInnerShadow(a.getFloat(R.styleable.MagicTextView_innerShadowRadius, 0), 
                                                                        a.getFloat(R.styleable.MagicTextView_innerShadowDx, 0), 
                                                                        a.getFloat(R.styleable.MagicTextView_innerShadowDy, 0),
                                                                        a.getColor(R.styleable.MagicTextView_innerShadowColor, 0xff000000));
                        }
                        
                        if(a.hasValue(R.styleable.MagicTextView_outerShadowColor)){
                                this.addOuterShadow(a.getFloat(R.styleable.MagicTextView_outerShadowRadius, 0), 
                                                                        a.getFloat(R.styleable.MagicTextView_outerShadowDx, 0), 
                                                                        a.getFloat(R.styleable.MagicTextView_outerShadowDy, 0),
                                                                        a.getColor(R.styleable.MagicTextView_outerShadowColor, 0xff000000));
                        }
                        
                        if(a.hasValue(R.styleable.MagicTextView_strokeColor)){
                                float strokeWidth = a.getFloat(R.styleable.MagicTextView_strokeWidth, 1);
                                int strokeColor = a.getColor(R.styleable.MagicTextView_strokeColor, 0xff000000);
                                float strokeMiter = a.getFloat(R.styleable.MagicTextView_strokeMiter, 10);
                                Join strokeJoin = null;
                                switch(a.getInt(R.styleable.MagicTextView_strokeJoinStyle, 0)){
                                case(0): strokeJoin = Join.MITER; break;
                                case(1): strokeJoin = Join.BEVEL; break;
                                case(2): strokeJoin = Join.ROUND; break;
                                }
                                this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter);
                        }
                }
        }
        
        public void setStroke(float width, int color, Join join, float miter){
                strokeWidth = width;
                strokeColor = color;
                strokeJoin = join;
                strokeMiter = miter;
        }
        
        public void setStroke(float width, int color){
                setStroke(width, color, Join.MITER, 10);
        }
        
        public void addOuterShadow(float r, float dx, float dy, int color){
                if(r == 0){ r = 0.0001f; }
                outerShadows.add(new Shadow(r,dx,dy,color));
        }
        
        public void addInnerShadow(float r, float dx, float dy, int color){
                if(r == 0){ r = 0.0001f; }
                innerShadows.add(new Shadow(r,dx,dy,color));
        }
        
        public void clearInnerShadows(){
                innerShadows.clear();
        }
        
        public void clearOuterShadows(){
                outerShadows.clear();
        }
        
        public void setForegroundDrawable(Drawable d){
                this.foregroundDrawable = d;
        }
        
        public Drawable getForeground(){
                return this.foregroundDrawable == null ? this.foregroundDrawable : new ColorDrawable(this.getCurrentTextColor());
        }

        
        @Override
        public void onDraw(Canvas canvas){
                super.onDraw(canvas);
                
                freeze();
                Drawable restoreBackground = this.getBackground();
                Drawable[] restoreDrawables = this.getCompoundDrawables();
                int restoreColor = this.getCurrentTextColor();
                
                this.setCompoundDrawables(null,  null, null, null);

                for(Shadow shadow : outerShadows){
                        this.setShadowLayer(shadow.r, shadow.dx, shadow.dy, shadow.color);
                        super.onDraw(canvas);
                }
                this.setShadowLayer(0,0,0,0);
                this.setTextColor(restoreColor);
                
                if(this.foregroundDrawable != null && this.foregroundDrawable instanceof BitmapDrawable){
                        generateTempCanvas();
                        super.onDraw(tempCanvas);
                        Paint paint = ((BitmapDrawable) this.foregroundDrawable).getPaint();
                        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
                        this.foregroundDrawable.setBounds(canvas.getClipBounds());
                        this.foregroundDrawable.draw(tempCanvas);
                        canvas.drawBitmap(tempBitmap, 0, 0, null);
                        tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                }

                if(strokeColor != null){
                        TextPaint paint = this.getPaint();
                        paint.setStyle(Style.STROKE);
                        paint.setStrokeJoin(strokeJoin);
                        paint.setStrokeMiter(strokeMiter);
                        this.setTextColor(strokeColor);
                        paint.setStrokeWidth(strokeWidth);
                        super.onDraw(canvas);
                        paint.setStyle(Style.FILL);
                        this.setTextColor(restoreColor);
                }
                if(innerShadows.size() > 0){
                        generateTempCanvas();
                        TextPaint paint = this.getPaint();
                        for(Shadow shadow : innerShadows){
                                this.setTextColor(shadow.color);
                                super.onDraw(tempCanvas);
                                this.setTextColor(0xFF000000);
                                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
                                paint.setMaskFilter(new BlurMaskFilter(shadow.r, BlurMaskFilter.Blur.NORMAL));
                                
                tempCanvas.save();
                tempCanvas.translate(shadow.dx, shadow.dy);
                                super.onDraw(tempCanvas);
                                tempCanvas.restore();
                                canvas.drawBitmap(tempBitmap, 0, 0, null);
                                tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                                
                                paint.setXfermode(null);
                                paint.setMaskFilter(null);
                                this.setTextColor(restoreColor);
                                this.setShadowLayer(0,0,0,0);
                        }
                }
                
                
                if(restoreDrawables != null){
                        this.setCompoundDrawablesWithIntrinsicBounds(restoreDrawables[0], restoreDrawables[1], restoreDrawables[2], restoreDrawables[3]);
                }
                this.setBackgroundDrawable(restoreBackground);
                this.setTextColor(restoreColor);

                unfreeze();
        }
        
        private void generateTempCanvas(){
            String key = String.format("%dx%d", getWidth(), getHeight());
            Pair<Canvas, Bitmap> stored = canvasStore.get(key);
            if(stored != null){
                tempCanvas = stored.first;
                tempBitmap = stored.second;
            }else{
            tempCanvas = new Canvas();
            tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            tempCanvas.setBitmap(tempBitmap);
            canvasStore.put(key, new Pair<Canvas, Bitmap>(tempCanvas, tempBitmap));
            }
        }

        
        // Keep these things locked while onDraw in processing
        public void freeze(){
                lockedCompoundPadding = new int[]{
                                getCompoundPaddingLeft(),
                                getCompoundPaddingRight(),
                                getCompoundPaddingTop(),
                                getCompoundPaddingBottom()
                };
                frozen = true;
        }
        
        public void unfreeze(){
                frozen = false;
        }
        
    
    @Override
    public void requestLayout(){
        if(!frozen) super.requestLayout();
    }
        
        @Override
        public void postInvalidate(){
                if(!frozen) super.postInvalidate();
        }
        
   @Override
    public void postInvalidate(int left, int top, int right, int bottom){
        if(!frozen) super.postInvalidate(left, top, right, bottom);
    }
        
        @Override
        public void invalidate(){
                if(!frozen)        super.invalidate();
        }
        
        @Override
        public void invalidate(Rect rect){
                if(!frozen) super.invalidate(rect);
        }
        
        @Override
        public void invalidate(int l, int t, int r, int b){
                if(!frozen) super.invalidate(l,t,r,b);
        }
        
        @Override
        public int getCompoundPaddingLeft(){
                return !frozen ? super.getCompoundPaddingLeft() : lockedCompoundPadding[0];
        }
        
        @Override
        public int getCompoundPaddingRight(){
                return !frozen ? super.getCompoundPaddingRight() : lockedCompoundPadding[1];
        }
        
        @Override
        public int getCompoundPaddingTop(){
                return !frozen ? super.getCompoundPaddingTop() : lockedCompoundPadding[2];
        }
        
        @Override
        public int getCompoundPaddingBottom(){
                return !frozen ? super.getCompoundPaddingBottom() : lockedCompoundPadding[3];
        }
        
        public static class Shadow{
                float r;
                float dx;
                float dy;
                int color;
                public Shadow(float r, float dx, float dy, int color){
                        this.r = r;
                        this.dx = dx;
                        this.dy = dy;
                        this.color = color;
                }
        }
}